package bcd

import (
	"bytes"
	"errors"
	"strings"
)

func EncodeFromStr(str string, bytesLength int) (out []byte) {
	s := LeftPadZero(str, bytesLength*2)

	return Encode([]byte(s))
}

// LeftPadZero 在字符串的左侧填充零，直到字符串达到指定的总长度。
func LeftPadZero(str string, totalLength int) string {
	// 首先检查输入参数的有效性
	if totalLength <= 0 {
		return str // 如果总长度小于等于0，则直接返回原始字符串（或可以考虑返回错误）
	}

	// 计算需要填充的零的数量
	padLength := totalLength - len(str)
	if padLength <= 0 {
		return str // 如果不需要填充，则直接返回原始字符串
	}

	// 创建一个由指定数量的零组成的字符串
	padStr := strings.Repeat("0", padLength)

	// 将填充的零字符串与原始字符串拼接，并返回结果
	return padStr + str
}

// DecodeToStr 解码字节数组，并根据 skipZero 参数决定是否跳过前导的 '0' 字符
func DecodeToStr(dataIn []byte, skipZero bool) string {
	out := Decode(dataIn)

	// 如果需要跳过前导的 '0' 字符
	if skipZero {
		// 查找第一个非 '0' 字符的索引
		firstNonZeroIndex := bytes.IndexFunc(out, func(r rune) bool {
			return r != '0'
		})
		// 如果找到了非 '0' 字符，则返回从该索引开始的子字符串
		if firstNonZeroIndex != -1 {
			return string(out[firstNonZeroIndex:])
		}
	}

	// 如果没有跳过前导 '0' 字符的需求，或者没有找到非 '0' 字符，则返回整个解码后的字符串
	return string(out)
}

func Encode(dataIn []byte) (out []byte) {
	dataInLen := len(dataIn)

	var posIn = 0
	var posOut = 0
	var nibbleFlag byte = 0
	var c byte = 0

	out = make([]byte, dataInLen/2)

	for ; posIn < dataInLen; posIn++ {
		c = dataIn[posIn]

		if c >= '0' && c <= '9' {
			c -= '0'
		} else {
			switch {
			case c == 'A' || c == 'a':
				c = 0x0a
			case c == 'B' || c == 'b':
				c = 0x0b
			case c == 'C' || c == 'c':
				c = 0x0c
			case c == 'D' || c == 'd':
				c = 0x0d
			case c == 'E' || c == 'e':
				c = 0x0e
			case c == 'F' || c == 'f':
				c = 0x0f
			default:
				return nil
			}
		}

		if nibbleFlag == 0 {
			c <<= 4
			out[posOut] |= c
			nibbleFlag = 1
		} else {
			out[posOut] |= c
			posOut++
			nibbleFlag = 0
		}
	}

	return
}

func Decode(dataIn []byte) (out []byte) {
	dataInLen := len(dataIn)

	var posIn = 0
	var posOut = 0

	out = make([]byte, dataInLen*2)

	for ; posIn < dataInLen; posIn++ {
		out[posOut] = nibbleToHexChar(dataIn[posIn] >> 4)
		posOut++

		out[posOut] = nibbleToHexChar(dataIn[posIn] & 0x0f)
		posOut++
	}

	return
}

func nibbleToHexChar(nibble byte) byte {
	if nibble <= 9 {
		return nibble + '0'
	} else {
		switch {
		case nibble == 0x0a:
			return 'a'
		case nibble == 0x0b:
			return 'b'
		case nibble == 0x0c:
			return 'c'
		case nibble == 0x0d:
			return 'd'
		case nibble == 0x0e:
			return 'e'
		case nibble == 0x0f:
			return 'f'
		default:
			return ' '
		}
	}
}

// =======

func CheckBCD(BcdValue uint32) bool {
	for i := 0; i < 8; i++ {
		if (BcdValue & 0x0F) > 0x09 {
			return false
		}

		BcdValue >>= 4
	}

	return true
}

func Uint32ToBCD(BinaryValue uint32) (uint32, error) {
	if BinaryValue <= 99999999 {
		var ValueToReturn uint32 = 0
		var factor uint32 = 10000000

		for i := 0; i < 8; i++ {
			ValueToReturn <<= 4

			var temp = BinaryValue

			temp /= factor
			ValueToReturn |= temp

			temp *= factor
			BinaryValue -= temp
			factor /= 10
		}

		return ValueToReturn, nil
	} else {
		return BinaryValue, errors.New("bad")
	}
}

func BCDtoUint32(CurrentBcdValue uint32) (uint32, error) {
	if !CheckBCD(CurrentBcdValue) {
		return CurrentBcdValue, errors.New("bad")
	}

	var ValueToReturn uint32 = 0
	var factor uint32 = 1

	for i := 0; i < 8; i++ {
		var temp = CurrentBcdValue
		temp &= 0x0F
		temp *= factor
		ValueToReturn += temp
		CurrentBcdValue >>= 4
		factor *= 10
	}

	return ValueToReturn, nil
}
